﻿using FB.Membership.Principal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;

namespace FB.Membership.Annotations
{
    public class AuthorizeOnlyAttribute : AuthorizeAttribute//, System.Web.Http.Filters.IFilter
    {
		private readonly IRolesService _rolesService;
		private readonly IUserService _userService;
		private string[] _rolesSplit;
		private string[] _usersSplit;

        public object Permission  { get; set; }

		public AuthorizeOnlyAttribute() : this(new AspNetMembershipProviderWrapper(), new AspNetRoleProviderWrapper())
		{
		}

        public AuthorizeOnlyAttribute(IUserService userService, IRolesService rolesService)
		{
			_userService = userService;
			_rolesService = rolesService;
		}

		protected override bool AuthorizeCore(HttpContextBase httpContext)
		{
			if (httpContext == null)
				throw new ArgumentNullException("httpContext");

			//never authorize someone who isn't logged in
			var user = httpContext.User;
			if (!user.Identity.IsAuthenticated)
				return false;

            #region checking services list

            var fbi = user.Identity as FBIdentity;
            if (null == fbi)
                return false;
            else
            {
                // change the source from Identity property(cookie) to database. (p.s. make the changes immediately effective)
                var serviceList = _userService.GetService(user.Identity.Name); //fbi.Services;
                if (!_userService.HasService(FB.Helpers.AppSettingsBase.AppKey.ToString(), user.Identity.Name, serviceList))
                    return false;
            }

            #endregion


            #region checking for authenticated user

            if (null == PermissionManager.PermissionSession)
            {
                // refresh permissions and roles
                RefreshRoleAndPermission();
            }

            //var userCheckResult = false;
            //var roleCheckResult = false;
            //var permCheckResult = false;


			//MSFT wasn't kind enough to make these protected, so on the first request go ahead and recacl these
            if (!string.IsNullOrEmpty(Users)) _usersSplit = SplitString(Users);
			if (!string.IsNullOrEmpty(Roles)) _rolesSplit = SplitString(Roles);

			//same user check as MSFT - not sure that anyone actually uses this feature though...
            //if (_usersSplit.Any() && !_usersSplit.Contains(user.Identity.Name, StringComparer.InvariantCultureIgnoreCase)) return false;
            //userCheckResult = !_usersSplit.Any() || (_usersSplit.Any() && !_usersSplit.Contains(user.Identity.Name, StringComparer.InvariantCultureIgnoreCase));
            if (null != _usersSplit && _usersSplit.Any() 
                && _usersSplit.Contains(user.Identity.Name, StringComparer.InvariantCultureIgnoreCase))
                return true;

            //is this user in one of the necessary roles?
            //if (_rolesSplit.Any() && _rolesService.Enabled && !_rolesSplit.Any(user.IsInRole)) return false;
            //roleCheckResult = !_rolesSplit.Any() || (_rolesSplit.Any() && _rolesService.Enabled && !_rolesSplit.Any(user.IsInRole));
            if (null != _rolesSplit &&
                _rolesSplit.Any() && _rolesService.Enabled && 
                _rolesSplit.Any(user.IsInRole))
                return true;


            //if (Permission != null) return (!(user is FBPrincipal) || !this.Permission.GetType().IsEnum) ? false : ((FBPrincipal)user).HasFlag(this.Permission);
            //permCheckResult = (Permission != null) ? ((!(user is FBPrincipal) || !this.Permission.GetType().IsEnum) ? false : ((FBPrincipal)user).HasFlag(this.Permission)) : true;
            if (null != Permission && (user is FBPrincipal) && 
                this.Permission.GetType().IsEnum && 
                ((FBPrincipal)user).HasFlag(this.Permission))
                return true;
                

            //return true;
            return null == _rolesSplit && null == _usersSplit && null == Permission;

            #endregion 
		}


        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            string[] acceptedTypes = filterContext.HttpContext.Request.AcceptTypes;
            if (filterContext.HttpContext.User.Identity.IsAuthenticated)
            {

                foreach (string type in acceptedTypes)
                {
                    // handle html request
                    if (type.Contains("html"))
                    {
                        if (filterContext.HttpContext.Request.IsAjaxRequest())
                            filterContext.Result = new ViewResult { ViewName = "_AccessDeniedPartial" };
                        else
                            filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
                        break;
                    }
                    // handle json request
                    else if (type.Contains("json"))
                    {
                        filterContext.Result = new JsonResult { Data = new { success = false, message = "Access denied." } };
                        break;
                    }
                    // handle xml request
                    else if (type.Contains("xml"))
                    {
                        //this will redirect to login page with forms auth you could instead serialize a custom xml payload and return here.
                        filterContext.Result = new HttpUnauthorizedResult();
                    }
                }
            }
            else
            {

                if (filterContext.HttpContext.Request.IsAjaxRequest()) // for unauthorized ajax request 
                    filterContext.Result = new PartialViewResult { ViewName = "_AjaxRedirectToLogin" };
                else if (acceptedTypes.Contains("application/json")) // for unauthorized json request 
                {
                    filterContext.Result = new JsonResult { Data = new { Status = false, Msg = "Access denied." }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
                }
                else
                    base.HandleUnauthorizedRequest(filterContext);
            }

        }



        #region private

        private static string[] SplitString(string original)
		{
			if (String.IsNullOrEmpty(original))
				return new string[0];

			var split = from piece in original.Split(',')
			            let trimmed = piece.Trim()
			            where !String.IsNullOrEmpty(trimmed)
			            select trimmed;
			return split.ToArray();
        }


        void RefreshRoleAndPermission()
        {
            var user = HttpContext.Current.User;
            if (user.Identity.IsAuthenticated)
            {
                var membership = System.Web.Security.Membership.GetUser(user.Identity.Name);
                PermissionManager.PermissionSession = PermissionManager.GetUserPermissions(membership);
                AspNetRoleProviderWrapper.RoleSession = System.Web.Security.Roles.GetRolesForUser(user.Identity.Name);
            }

            
        }


        #endregion

    }
}
